using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Framework.Save;
using LitJson;
using UnityEngine;

namespace GameCore
{
    [Serializable]
    public class Shape : GameData, IEnumerable<Vector2Int>
    {
        [SerializeField] private List<Vector2Int> shape = new();
        
        public Shape() { }
        public Shape(IEnumerable<Vector2Int> shape) => this.shape.AddRange(shape.Distinct());
        public Shape(params Vector2Int[] shape) => this.shape.AddRange(shape.Distinct());
        
        public IEnumerator<Vector2Int> GetEnumerator() => shape.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => shape.GetEnumerator();
        
        public int Size => shape.Count;
        public Shape Clone() => new(this);
        
        #region Shape Transformation

        public Shape Translate(Vector2Int delta)
        {
            for (int i = 0; i < shape.Count; i++)
                shape[i] += delta;
            return this;
        }
        
        public Shape Translate(int x, int y) => Translate(new Vector2Int(x, y));
        
        /// <summary> Rotate the shape 90° clockwise </summary>
        public Shape Rotate90()
        {
            for (int i = 0; i < shape.Count; i++)
                shape[i] = new Vector2Int(shape[i].y, -shape[i].x);
            return this;
        }
    
        /// <summary> Rotate the shape 180° clockwise </summary>
        public Shape Rotate180()
        {
            for (int i = 0; i < shape.Count; i++)
                shape[i] = new Vector2Int(-shape[i].x, -shape[i].y);
            return this;
        }
        
        /// <summary> Rotate the shape 270° clockwise (90° CCW). </summary>
        public Shape Rotate270()
        {
            for (int i = 0; i < shape.Count; i++)
                shape[i] = new Vector2Int(-shape[i].y, shape[i].x);
            return this;
        }

        /// <summary> Flip the shape alone y = 0 axis </summary>
        public Shape FlipX()
        {
            for (int i = 0; i < shape.Count; i++)
                shape[i] = new Vector2Int(-shape[i].x, shape[i].y);
            return this;
        }

        /// <summary> Flip the shape alone x = 0 axis </summary>
        public Shape FlipY()
        {
            for (int i = 0; i < shape.Count; i++)
                shape[i] = new Vector2Int(shape[i].x, -shape[i].y);
            return this;
        }
        
        #endregion
        
        #region Cell Management
        
        public Shape AddCells(IEnumerable<Vector2Int> cells)
        {
            var tempShape = new HashSet<Vector2Int>(shape);
            foreach (var cell in cells)
                tempShape.Add(cell);
            shape = new List<Vector2Int>(tempShape);
            return this;
        }
        
        public Shape AddCells(params Vector2Int[] cells)
        {
            foreach (var cell in cells)
            {
                shape.Add(cell);
            }
            return this;
        }

        public Shape AddCell(Vector2Int cell)
        {
            shape.Add(cell);
            return this;
        }

        public Shape RemoveCells(IEnumerable<Vector2Int> cells)
        {
            var tempShape = new List<Vector2Int>(shape);
            foreach (var cell in cells)
                tempShape.Remove(cell);
            shape = new List<Vector2Int>(tempShape);
            return this;
        }
        
        public Shape RemoveCells(params Vector2Int[] cells) => RemoveCells(cells);
        public Shape RemoveCell(Vector2Int cell) => RemoveCells(cell);

        public Shape AddShape(Shape other) => AddCells(other);
        public Shape RemoveShape (Shape other) => RemoveCells(other);
        
        public bool ContainsCell(Vector2Int cell) => shape.Contains(cell);

        #endregion
        
        #region Shape Comparation

        public int Overlap(Shape other)
        {
            var tempCells = new HashSet<Vector2Int>(shape);
            int ret = 0;
            foreach (var cell in other)
            {
                if (tempCells.Contains(cell))
                    ret++;
            }

            return ret;
        }

        public bool Contains(Shape other)
        {
            var tempCells = new HashSet<Vector2Int>(shape);
            foreach (var cell in other)
            {
                if (!tempCells.Contains(cell))
                    return false;
            }
            return true;
        }
        
        #endregion

        #region Serialization

        public override JsonData Serialize()
        {
            var data = new JsonData();
            data.SetJsonType(JsonType.Array);
            foreach (var cell in shape)
            {
                var item = new JsonData();
                item.SetJsonType(JsonType.Object);
                item["x"] = cell.x;
                item["y"] = cell.y;
                data.Add(item);
            }
            return data;
        }

        public override void Deserialize(JsonData data)
        {
            shape.Clear();
            if (data == null || !data.IsArray) return;
            var cells = new HashSet<Vector2Int>();
            for (int i = 0; i < data.Count; i++)
            {
                var item = data[i];
                int x = (int)item["x"];
                int y = (int)item["y"];
                cells.Add(new Vector2Int(x, y));
            }
            shape.AddRange(cells);
        }
        
        #endregion
    }
}